﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MvcContrib.TestHelper;
using MvcContrib.TestHelper.Fakes;
using ProgressTracker.Domain;
using ProgressTracker.Domain.Contracts;
using ProgressTracker.Domain.Messages;
using ProgressTracker.WebUI.Controllers;
using ProgressTracker.WebUI.Models.Task;

namespace ProgressTracker.Tests
{
    [TestClass]
    public class TaskControllerTests
    {
        private Mock<ITaskService> _taskService;
        private const int TaskId = 1;
        private const string TaskName = "Test";
        private const string TaskDescription = "Test Description";

        [TestInitialize()]
        public void Initialize()
        {
            _taskService = new Mock<ITaskService>();
        }

        [TestMethod]
        public void Test_Index_Correctly_Returns_View()
        {
            var taskController = CreateController();

            var result = taskController.Index();

            result.AssertViewRendered();
        }

        [TestMethod]
        public void Test_Add_Get_Correctly_Returns_Partial_View()
        {
            var taskController = CreateController();

            var result = taskController.Add();

            result.AssertPartialViewRendered();
        }

        [TestMethod]
        public void Test_Add_Post_Correctly_Adds_New_Task()
        {
            _taskService.Setup(x => x.AddTask(It.Is<AddTaskRequest>(
                t => t.Name == TaskName &&
                t.Description == TaskDescription))).Verifiable();

            var taskController = CreateController();

            taskController.Add(new AddNewTaskModel { 
                Name = TaskName,
                Description = TaskDescription
            });

            _taskService.Verify();
        }

        [TestMethod]
        public void Test_Add_Post_Redirects_To_Index_On_Successful_Add()
        {
            _taskService.Setup(x => x.AddTask(It.IsAny<AddTaskRequest>()));

            var taskController = CreateController();

            var result = taskController.Add(new AddNewTaskModel());

            result.AssertActionRedirect().ToAction("Index");
        }

        [TestMethod]
        public void Test_Archive_Archives_Correct_Task()
        {
            _taskService.Setup(x => x.GetTask(TaskId)).Returns(
                new Task
                {
                    Id = TaskId,
                    Name = TaskName,
                    Description = TaskDescription
                });

            _taskService.Setup(x => x.ArchiveTask(It.Is<int>(
                t => t == TaskId))).Verifiable();

            var taskController = CreateController();
            taskController.Archive(TaskId);

            _taskService.Verify();
        }

        [TestMethod]
        public void Test_Archive_Redirects_To_Index_On_Successful_Archive()
        {
            _taskService.Setup(x => x.GetTask(TaskId)).Returns(
                new Task
                {
                    Id = 1,
                    Name = TaskName,
                    Description = TaskDescription
                });

            _taskService.Setup(x => x.DeleteTask(It.IsAny<int>()));

            var taskController = CreateController();
            var result = taskController.Archive(TaskId);

            result.AssertActionRedirect().ToAction("Index");
        }

        [TestMethod]
        public void Test_Edit_Get_Correctly_Returns_Partial_View()
        {
            _taskService.Setup(x => x.GetTask(TaskId)).Returns(new Task
                {
                    Id = TaskId,
                    Name = TaskName,
                    Description = TaskDescription
                }).Verifiable();

            var taskController = CreateController();
            var result = taskController.Edit(TaskId);

            _taskService.Verify();
            result.AssertPartialViewRendered();
        }

        [TestMethod]
        public void Test_Edit_Post_Correctly_Updates_Task()
        {
            _taskService.Setup(x => x.UpdateTask(It.Is<UpdateTaskRequest>(
                t => t.Id == TaskId &&
                t.Name == TaskName &&
                t.Description == TaskDescription))).Verifiable();

            var taskController = CreateController();
            taskController.Edit(new EditTaskModel
                {
                    Id = TaskId,
                    Name = TaskName,
                    Description = TaskDescription
                });

            _taskService.Verify();
        }

        [TestMethod]
        public void Test_Edit_Post_Redirects_To_Index_On_Successful_Edit()
        {
            _taskService.Setup(x => x.UpdateTask(It.IsAny<UpdateTaskRequest>()));

            var taskController = CreateController();
            var result = taskController.Edit(new EditTaskModel
            {
                Id = TaskId,
                Name = TaskName,
                Description = TaskDescription
            });

            result.AssertActionRedirect().ToAction("Index");
        }

        private TaskController CreateController()
        {
            //need to generate fake http context and fake principal to utilize 'User' variable.
            var user = new FakePrincipal(new FakeIdentity("fake@fake.com"), null);

            var builder = new TestControllerBuilder();
            builder.HttpContext.User = user;

            var taskController = new TaskController(_taskService.Object);
            builder.InitializeController(taskController);

            return taskController;
        }
    }
}
